package net.yxsoft.service;

import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONObject;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.protobuf.ByteString;
import com.jfinal.plugin.activerecord.Db;
import com.jfinal.plugin.activerecord.Record;
import net.yxsoft.bean.contract.EvidenceItem;
import net.yxsoft.chainmaker.CryptoUtils;
import net.yxsoft.chainmaker.InitClient;
import net.yxsoft.kit.Helper;
import net.yxsoft.model.formdesigner.FnFormData;
import net.yxsoft.service.common.BaseService;
import org.chainmaker.pb.common.ChainmakerTransaction;
import org.chainmaker.pb.common.ResultOuterClass;
import org.chainmaker.pb.config.ChainConfigOuterClass;
import org.chainmaker.sdk.ChainClient;
import org.chainmaker.sdk.SdkException;
import org.chainmaker.sdk.User;

import java.math.BigInteger;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

public class EvidenceService extends BaseService {

    public static final EvidenceService me = new EvidenceService();

    /**
     * 将合约的所有权转移给指定的地址
     *
     * @param chainClient    区块链客户端
     * @param chainAccoutCID 区块链账号的CID
     * @param contractName   合约名称
     * @return 转移所有权是否成功的布尔值
     * @throws Exception 发生错误时抛出异常
     */
    public static boolean transferOwner(ChainClient chainClient, String contractName, String chainAccoutCID) throws Exception {

        Record rd = Db.findFirst("select * from fn_form_data where attrib_01 = ? and  conflict_id = ?", "d3720c95544c48d4875c61ff57685233", chainAccoutCID);

        if (rd == null) {
            return false;
        }
        User user = InitClient.getClientUser(rd.getStr("ATTRIB_05"), rd.getStr("ATTRIB_13").getBytes(), rd.getStr("ATTRIB_20").getBytes(), rd.getStr("ATTRIB_59").getBytes(), rd.getStr("ATTRIB_60").getBytes());        Map<String, byte[]> paramMap = new HashMap<>();
        String address = CryptoUtils.certToAddrStr(user.getCertificate(), ChainConfigOuterClass.AddrType.ETHEREUM);
        paramMap.put("account", address.getBytes());

        ResultOuterClass.TxResponse responseInfo = null;
        try {
            responseInfo = chainClient.invokeContract(contractName, "transferOwner",
                    null, paramMap, 10000, 10000);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("checkEvidenceExist 执行合约结果：");
        System.out.println(responseInfo);
        return responseInfo.getTxId() != null ? true : false;

    }

    /**
     * 将合约的所有权转移给指定的地址
     *
     * @param chainClient    区块链客户端
     * @param orgId 区块链组织ID
     * @param certUserName 区块链用户账号
     * @param contractName   合约名称
     * @return 转移所有权是否成功的布尔值
     * @throws Exception 发生错误时抛出异常
     */
    public static boolean transferOwnerTwo(ChainClient chainClient, String orgId, String certUserName , String contractName) throws Exception {

        Record signRecord = Db.findFirst("SELECT cert,private_key,cert_use FROM cmb_cert WHERE cert_user_name = ? and org_id = ?  and cert_use = '0' ", certUserName, orgId);
        Record tlsRecord = Db.findFirst("SELECT cert,private_key,cert_use FROM cmb_cert WHERE cert_user_name = ? and org_id = ?  and cert_use = '1' ", certUserName, orgId);
        if (signRecord==null || tlsRecord==null) {
            return false;
        }
        User user = InitClient.getClientUser(orgId, signRecord.getStr("private_key").getBytes(), signRecord.getStr("cert").getBytes(), tlsRecord.getStr("private_key").getBytes(), tlsRecord.getStr("cert").getBytes());        Map<String, byte[]> paramMap = new HashMap<>();
        String address = CryptoUtils.certToAddrStr(user.getCertificate(), ChainConfigOuterClass.AddrType.ETHEREUM);
        paramMap.put("account", address.getBytes());

        ResultOuterClass.TxResponse responseInfo = null;
        try {
            responseInfo = chainClient.invokeContract(contractName, "transferOwner",
                    null, paramMap, 10000, 10000);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("checkEvidenceExist 执行合约结果：");
        System.out.println(responseInfo);
        return responseInfo.getTxId() != null ? true : false;

    }



    /**
     * 获取指定合约的证据项
     *
     * @param chainClient  区块链客户端
     * @param evdcHash     证据项哈希值
     * @param contractName 合约名称
     * @return 证据项对象
     * @throws Exception 发生错误时抛出异常
     */
    public static EvidenceItem getEvidence(ChainClient chainClient, String evdcHash, String contractName) throws Exception {

        Map<String, byte[]> paramMap = new HashMap<>();
        paramMap.put("evdcHash", evdcHash.getBytes());

        ResultOuterClass.TxResponse responseInfo = null;
        try {
            responseInfo = chainClient.queryContract(contractName, "getEvidence",
                    null, paramMap, 10000);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("getEvidence 执行合约结果：");
        System.out.println(responseInfo);

        String str = ByteString.copyFromUtf8(responseInfo.getContractResult().getResult().toStringUtf8()).toStringUtf8();

        return jsonObjectToEvidenceItem(str);

    }

    /**
     * 检查指定合约中是否存在指定的证据项
     *
     * @param chainClient  区块链客户端
     * @param evdcHash     证据项哈希值
     * @param contractName 合约名称
     * @return 检查结果的布尔值
     * @throws Exception 发生错误时抛出异常
     */
    public static boolean checkEvidenceExist(ChainClient chainClient, String evdcHash, String contractName) throws Exception {

        Map<String, byte[]> paramMap = new HashMap<>();
        paramMap.put("evdcHash", evdcHash.getBytes());

        ResultOuterClass.TxResponse responseInfo = null;
        try {
            responseInfo = chainClient.queryContract(contractName, "checkEvidenceExist",
                    null, paramMap, 10000);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("checkEvidenceExist 执行合约结果：");
        System.out.println(responseInfo);
        return responseInfo.getTxId() != null ? true : false;

    }

    /**
     * 获取指定合约中的证据项数量
     *
     * @param chainClient  区块链客户端
     * @param contractName 合约名称
     * @return 证据项数量
     * @throws Exception 发生错误时抛出异常
     */
    public static Integer getEvidenceSize(ChainClient chainClient, String contractName) throws Exception {

        ResultOuterClass.TxResponse responseInfo = null;
        try {
            responseInfo = chainClient.queryContract(contractName, "getEvidenceSize",
                    null, null, 10000);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("getEvidenceSize 查询合约结果：");
        System.out.println(responseInfo);
        try {
            String resultString = responseInfo.getContractResult().getResult().toStringUtf8();
            Integer result = Integer.parseInt(resultString);
            return result;
        } catch (Exception e) {
//            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 获取证据项列表
     *
     * @param chainClient  区块链客户端
     * @param pagesize     分页大小
     * @param pagenum      分页号
     * @param contractName 合约名称
     * @return 证据项列表
     * @throws Exception 发生错误时抛出异常
     */
    public static ArrayList<EvidenceItem> getEvidences(ChainClient chainClient, int pagesize, int pagenum, String contractName) throws Exception {

        Map<String, byte[]> paramMap = new HashMap<>();

        JSONObject page = new JSONObject();
        page.put("PageSize", pagesize);
        page.put("PageNum", pagenum);

        paramMap.put("data", page.toString().getBytes());

        ResultOuterClass.TxResponse responseInfo = null;
        try {
            responseInfo = chainClient.queryContract(contractName, "getEvidences",
                    null, paramMap, 10000);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("getEvidences 执行合约结果：");
        System.out.println(responseInfo);

        Gson gson = new Gson();
        if (responseInfo.getContractResult().getResult() == null || responseInfo.getContractResult().getMessage().equals("Fail")) {
            return null;
        }
        String str = ByteString.copyFromUtf8(responseInfo.getContractResult().getResult().toStringUtf8()).toStringUtf8();
        // 将字符串转换为JsonArray
        JsonArray jsonArray = gson.fromJson(str, JsonArray.class);
        if (jsonArray == null) {
            return null;
        }

        ArrayList<EvidenceItem> evidenceItems = new ArrayList<>();

        for (int i = 0; i < jsonArray.size(); i++) {
            String objstr = jsonArray.get(i).toString();
            EvidenceItem evidenceItem = jsonObjectToEvidenceItem(objstr);
            if (evidenceItem == null) {
                return null;
            }
            evidenceItems.add(evidenceItem);
        }

        return evidenceItems;
    }

    /**
     * 将所有证据项存入数据库
     *
     * @param chainClient  区块链客户端
     * @param contractName 合约名称
     * @return 存储成功返回 true，否则返回 false
     * @throws Exception 发生错误时抛出异常
     */
    public static boolean getAllEvidencesToDb(ChainClient chainClient, String contractName) throws Exception {

        Integer allSize = getEvidenceSize(chainClient, contractName);

        if (allSize == 0) {
            return true;
        }

        int pagesize = 100;

        // 相除并取商
        int pagenum = allSize / pagesize;
        // 相除并取余数
        int remainder = allSize % pagesize;

        if (remainder > 0) {
            pagenum++;
        }

        //一页一页取，每次100条
        for (int ii = 1; ii <= pagenum; ii++) {
            JSONObject page = new JSONObject();
            page.put("PageSize", pagesize);
            page.put("PageNum", ii);
            Map<String, byte[]> paramMap = new HashMap<>();
            paramMap.put("data", page.toString().getBytes());

            ResultOuterClass.TxResponse responseInfo = null;
            try {
                responseInfo = chainClient.queryContract(contractName, "getEvidences",
                        null, paramMap, 10000);
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println("getEvidences 执行合约结果：");
            System.out.println(responseInfo);

            Gson gson = new Gson();
            if (responseInfo.getContractResult().getResult() == null || responseInfo.getContractResult().getMessage().equals("Fail")) {
                return false;
            }
            String str = ByteString.copyFromUtf8(responseInfo.getContractResult().getResult().toStringUtf8()).toStringUtf8();
            // 将字符串转换为JsonArray
            JsonArray jsonArray = gson.fromJson(str, JsonArray.class);
            if (jsonArray == null) {
                return false;
            }

            for (int i = 0; i < jsonArray.size(); i++) {
                String objstr = jsonArray.get(i).toString();
                JSONObject evidenceObject = JSONUtil.toBean(objstr, JSONObject.class);

                FnFormData fnFormData = new FnFormData();
                String sEvdcHash = evidenceObject.get("EvdcHash").toString();
                String sName = evidenceObject.get("Name").toString();
                String sCreateTM = evidenceObject.get("CreateTM").toString();
                String sEvidenceType = evidenceObject.get("EvidenceType").toString();
                String sRemark = evidenceObject.get("Remark").toString();
                String sTxHash = evidenceObject.get("TxHash").toString();

                fnFormData.setAttrib12(sEvdcHash);
                fnFormData.setAttrib32(sName);
                fnFormData.setAttrib33(sCreateTM);
                fnFormData.setAttrib04(sEvidenceType);
                fnFormData.setAttrib37(sRemark);
                fnFormData.setAttrib58(sTxHash);

                Record rd = Db.findFirst("select * from fn_form_data where attrib_01 = ? and attrib_12 = ?", "080608a331c4435ba313a487beca105a", sEvdcHash);

                ChainmakerTransaction.TransactionInfo transactionInfo = null;

                if (rd != null) {   //已经存在
                    fnFormData.setConflictId(rd.getStr("conflict_id"));
                    if (!fnFormData.update()) {
                        System.out.println("\n链上存证数据缓存更新失败, evchash: " + sEvdcHash);
                    }
                } else {

                    if (chainClient == null) {
                        InitClient.inItChainClient();
                    }

                    try {
                        transactionInfo = chainClient.getTxByTxId(fnFormData.getAttrib58(), 10000);
                    } catch (SdkException e) {
                        e.printStackTrace();
                    }

                    fnFormData.setAttrib40(transactionInfo.getTransaction().getPayload().getChainId());
                    fnFormData.setAttrib41(transactionInfo.getTransaction().getPayload().getContractName());
                    fnFormData.setAttrib42("");  //合约版本号
                    fnFormData.setAttrib49(String.valueOf(transactionInfo.getTransaction().getPayload().getTimestamp()));

                    fnFormData.setAttrib43(transactionInfo.getTransaction().getPayload().getTxId());
                    fnFormData.setAttrib44(String.valueOf(transactionInfo.getBlockHeight()));
                    fnFormData.setAttrib45(transactionInfo.getTransaction().getSender().getSigner().getOrgId());

                    String cid = Helper.bulidUUID();
                    fnFormData.setConflictId(cid);
                    fnFormData.setRowId(cid);
                    fnFormData.setAttrib01("080608a331c4435ba313a487beca105a");   //共享数据动态表单

                    if (!fnFormData.save()) {
                        System.out.println("\n链上存证数据缓存保存失败, evchash: " + sEvdcHash);
                    }
                }

            }
        }
        return true;
    }

    public static EvidenceItem jsonObjectToEvidenceItem(String jsonstr) {

        if (jsonstr.equals("")) {
            return null;
        }

        JSONObject jsonObject = JSONUtil.toBean(jsonstr, JSONObject.class);

        EvidenceItem evidenceItem = null;
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssXXX");
        if (jsonObject.size() > 0) {
            evidenceItem = new EvidenceItem(jsonObject.get("Name").toString().getBytes(), jsonObject.getBigInteger("CreateTM"), jsonObject.getBigInteger("EvidenceType"), jsonObject.get("Remark").toString().getBytes(), jsonObject.get("TxHash").toString().getBytes());
        } else {
            evidenceItem = new EvidenceItem("".getBytes(), new BigInteger("0"), new BigInteger("0"), "".getBytes(), "".getBytes());
        }

        return evidenceItem;


    }

}
